/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package compumundohipermegared;

import ConstantesProcotolo.Protocolo;
import clasesJXTA.AdministradorJXTA;
import clasesJXTA.NodoRemoto;
import java.io.*;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import net.jxta.socket.JxtaMulticastSocket;
import net.jxta.socket.JxtaSocket;
import rsa.mundo.Rsa;
import seguridad.ManejadorCertificados;
import sun.security.x509.X509CertImpl;

/**
 *
 * @author asistente
 */
public class Buscador implements Serializable {

    private byte[] bytesLlaveDescifrada;
    private String claveDescifrada;
    private String rutaCertDelQueEnvia;
    private String rutaFotoRecibida;
    /**
     * Indicador de versiÃ³n para la serializaciÃ³n
     */
    private static final long serialVersionUID = 100L;
    /**
     * busca multicast sockets
     */
    private MulticastSocket ms;
    //acÃ¡ va todo lo de jxta
    private AdministradorJXTA administradorJxta;
    private Compumundohipermegared padre;

    public Buscador(AdministradorJXTA administradorJxta, Compumundohipermegared padre) {
        try {
            this.administradorJxta = administradorJxta;
            this.padre = padre;
            //inicializa el soxket multicast
            ms = new JxtaMulticastSocket(administradorJxta.getNetPeerGroup(), administradorJxta.getPropagatePipeAdv());
            InetAddress group = InetAddress.getByName(Compumundohipermegared.DIRECCION_MULTICAST);
            System.out.println(group.toString());
            ms.setBroadcast(true);
            ms.joinGroup(group);
            ms.setSoTimeout(0);
            System.out.println(ms);
        } catch (IOException ex) {
            Logger.getLogger(Buscador.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    /**
     * El flujo de salida asociado con el canal de comunicaciÃ³n
     */
    private PrintWriter out;
    /**
     * El flujo de entrada asociado con el canal de comunicaciÃ³n
     */
    private BufferedReader in;
    private OutputStream outputStream;
    private InputStream inputStream;
    // el constructor deberÃ­a tener lo necesario de jxta

    /**
     * busca fotos por el nombre que se inica por parametro, este metodo se
     * encarga de hacer la comunicaciÃ³n con los demÃ¡s peers,
     *
     * @param nombreBuscado
     * @return un arraylist de fotos con las fotos que encontrÃ³ de los demÃ¡s
     * peers
     */
    public ArrayList<Foto> buscarFotosPorNombre(String nombreBuscado) {
        ArrayList<Foto> resp = new ArrayList<Foto>();
        return resp;
    }

    /**
     * busca fotos por las personas que aparecen en la foto
     *
     * @param personasBuscadas, los nombres de las personas que se esta buscando
     * @return un arraylist de fotos con las fotos que encontrÃ³ de los demÃ¡s
     * peers
     */
    public ArrayList<Foto> buscarFotosPorPersonas(String[] personasBuscadas) {
        ArrayList<Foto> resp = new ArrayList<Foto>();
        return resp;
    }

    /**
     * Descarga la foto indicada por parametro, en la foto dice quiÃ©n tiene la
     * foto este metodo hace toda la comunicaciÃ³n con el otro peer y baja la
     * foto a donde debe estar
     *
     * @param fotoADescargar
     */
    public void descargarFoto(String fotoADescargar, NodoRemoto nr) throws IOException 
    {
        System.out.println(nr.getDireccionIP());
        System.out.println(nr.getPipeAdvertisement().toString());
        JxtaSocket socketDescarga = new JxtaSocket(administradorJxta.getNetPeerGroup(), nr.getPipeAdvertisement());

        // crea los flujos de entrada-salida
        out = new PrintWriter(socketDescarga.getOutputStream(), true);
        in = new BufferedReader(new InputStreamReader(socketDescarga.getInputStream()));

        outputStream = socketDescarga.getOutputStream();
        inputStream = socketDescarga.getInputStream();

        enviarCertificado( );
        recibirLlaveSimetrica( );
        
        out.println(Protocolo.ENVIEME_LA_FOTO + ":" + fotoADescargar);
        String mensaje = in.readLine( );
        System.out.println( "llegÃ³ el mensaje" +mensaje );
        
        //lee la respuesta, puede ser que no la tenga o que si la tenga
        
        System.out.println("Recibi el nesaje: " + mensaje);
        
        if (mensaje.equals(Protocolo.AHI_VA_EL_OBJETO.toString())) {
            try {
                System.out.println("Recibiendo objeto...");
                ObjectInputStream ois = new ObjectInputStream(inputStream);
                Foto fotoRecibida = (Foto) ois.readObject();

                System.out.println("acabo de recibir el objeto " + fotoRecibida.getUbicacionArchivo());
                // acÃ¡ cambia la informaciÃ³n del objeto recibido
                fotoRecibida.setEsMia(false);
                fotoRecibida.setTiempoDescargada(new Date());
                System.out.println("AHORA ESPERO EL ARCHIVO");

                mensaje = in.readLine();
                System.out.println(mensaje);
                if (mensaje.startsWith(Protocolo.AHI_VA_EL_ARCHIVO.toString())) {
                    int tamanioFoto = Integer.valueOf(mensaje.split(":")[1]);

                    System.out.println("Voy a recibir una foto de tamanio: " + tamanioFoto);

                    int bytesRead;
                    int current = 0;
                    byte[ ] mybytearray = new byte[tamanioFoto];
                    String rutaDestino = Compumundohipermegared.CARPETA_DE_FOTOS + "externa" + fotoRecibida.getId() + "." + fotoRecibida.getFormato();
                    FileOutputStream fos = new FileOutputStream(rutaDestino);
                    // aca le tiene que cambiar la ruta a la foto
                    fotoRecibida.setUbicacionArchivo(rutaDestino);


                    BufferedOutputStream bos = new BufferedOutputStream(fos);
                    System.out.println("Leyendo Archivo");
                    //bytesRead = inputStream.read(mybytearray, 0, mybytearray.length);
                    //bytesRead = inputStream.read(mybytearray);

                    //System.out.println("y faltan " + inputStream.available());
                    //current = bytesRead;

                    // asi estaba en internet
                    do {
                        bytesRead =
                                inputStream.read(mybytearray, current, (mybytearray.length - current));
                        System.out.println("He leido " + current + " bytes, y acabo de leer" + bytesRead);
                        if (bytesRead >= 0) {
                            current += bytesRead;
                        }
                    } while (bytesRead > 0);

                    System.out.println("Termine de leerlo");
                    bos.write(mybytearray, 0, current);
                    bos.flush();


                    bos.close();

                    System.out.println("Listo, ya lo recibi");
                    System.out.println("ahora espero la firma...");
                    ////////////////////////////////////
                    ///////////firma!!!!!!
                    ////////////////////////////////////
                    recibirFirma();
                    mensaje = in.readLine();
                    recibirCertificado(mensaje);
                    boolean firma = validarFirma();
                    ////////////////////////////////////
                    ///////////firma!!!!!!
                    ////////////////////////////////////
                    System.out.println("Ahora lo voy a descifrar");
                    ManejadorCertificados.descifrarArchivo(rutaDestino, claveDescifrada);
                    ManejadorCertificados.cifrarArchivo(rutaDestino,padre.contrasenaEscogida );
                    // publica la foto 
                    padre.publicarFoto(fotoRecibida);
                    padre.agregarFotoRecibida(fotoRecibida);
                    rutaFotoRecibida=fotoRecibida.getUbicacionArchivo();
                }
                
                in.close();
               
            } 
            catch (Exception ex)
            {
                Logger.getLogger(Buscador.class.getName()).log(Level.SEVERE, null, ex);
            }


        } else if (mensaje.equals(Protocolo.NO_LA_TENGO.toString())) {
            System.out.println("Paila, ya no la tiene");
        }
    }

    /**
     *
     * @param foto
     */
    public void buscarFotoMasParecida(Foto foto)
    {
        System.out.println("buscarFotoMasParecida( foto ); 2");
        try {

            //el mensaje incluye el id de la foto para que no se devuelva la misma foto si otro la tiene


            // System.out.println("intet address del socket: "+ms.getInetAddress().toString());

            String mensaje = Protocolo.BLOQUEENSE.toString();
            DatagramPacket dp = new DatagramPacket(mensaje.getBytes(), mensaje.length());

            for (int i = 0; i < Compumundohipermegared.VECES_REPETIR_MENSAJES; i++) {
                ms.send(dp);
            }

            mensaje = Protocolo.ENVIEME_FOTO_MAS_PARECIDA + ":" + foto.getAnchoImagen() + ":" + foto.getAltoImagen() + ":" + foto.getId();

            // los sockets tienen que tener el mismo id para poder comunicarse entre sÃ­
            dp = new DatagramPacket(mensaje.getBytes(), mensaje.length());
            ms.send(dp);

            byte[] buf = new byte[1000];
            DatagramPacket recv = new DatagramPacket(buf, buf.length);
            ms.receive(recv);

            System.out.println("Esperando Respuestas");
            // ahora se queda esperando respuestas, 

            int numRespuestas = Compumundohipermegared.LIMITE_RESPUESTAS_FOTO_MAS_PARECIDA;
            int tiempoLimite = Compumundohipermegared.LIMITE_TIEMPO_RESPUESTA_FOTO_MAS_PARECIDA_SEGUNDOS;
            long tiempoInicial = System.currentTimeMillis();
            int numActualRespuestas = 0;

            boolean puedoSeguir = true;
            String idFotoMasParecida = "";

            while (puedoSeguir) {

                boolean numeroRespuestasAlcanzado = numRespuestas - numActualRespuestas <= 0;
                boolean tiempoLimiteAlcanzado = System.currentTimeMillis() - tiempoInicial > tiempoLimite * 1000;

                puedoSeguir = !(numeroRespuestasAlcanzado || tiempoLimiteAlcanzado);

                int puntajeMinimo = Integer.MAX_VALUE;


                if (puedoSeguir) {
                    try {
                        ms.setSoTimeout(tiempoLimite * 1000);
                        ms.receive(recv);

                        mensaje = new String(recv.getData(), 0);

                        System.out.println("llego la respuesta: " + mensaje);

                        if (!mensaje.startsWith(Protocolo.ENVIEME_FOTO_MAS_PARECIDA.toString())) {
                            numActualRespuestas++;

                            String[] partesResp = mensaje.split(":");
                            String idRecibido = partesResp[0];
                            String puntajeStr = partesResp[1];
                            int puntajeRecibido = Integer.parseInt(puntajeStr);
                            if (puntajeRecibido < puntajeMinimo) {
                                puntajeMinimo = puntajeRecibido;
                                padre.actualizarPuntajeMinimo( puntajeMinimo );
                                idFotoMasParecida = idRecibido;
                                padre.actualizarIdFotoMasParecida( idFotoMasParecida );
                            }
                        }


                    } catch (Exception e) {
                    }

                }


            }



            System.out.println("Termine de obtener respuestas");

            mensaje = Protocolo.TERMINE_RECIBIR_FOTOS.toString();
            dp = new DatagramPacket(mensaje.getBytes(), mensaje.length());
            // envÃ­a el mensaje varias veces por si se pierfde
            for (int i = 0; i < Compumundohipermegared.VECES_REPETIR_MENSAJES; i++) {
                ms.send(dp);
            }

            padre.buscarPorID(idFotoMasParecida);
            
            padre.encontreLaFotoMasParecida( );
        } catch (IOException e) {
            System.out.println("Se toteooooooooooooooooooooooooo");
            e.printStackTrace();
        }
    }

    public void guardarEstadosDelSistema(String nombreArchivo) {
        try {
            MulticastSocket ms = new JxtaMulticastSocket(administradorJxta.getNetPeerGroup(), administradorJxta.getPropagatePipeAdv());
            InetAddress group = InetAddress.getByName(Compumundohipermegared.DIRECCION_MULTICAST_GUARDAR);
            ms.setBroadcast(true);
            ms.joinGroup(group);
            ms.setSoTimeout(0);

            String mensaje = Protocolo.GUARDEN.toString();
            DatagramPacket dp = new DatagramPacket(mensaje.getBytes(), mensaje.length());

            ms.send(dp);
        } catch (IOException e) {
            System.out.println("No pudo guardar el estado");
            e.printStackTrace();
        }
    }

    /**
     * les pregunta a los demÃ¡s peers si estÃ¡n bloqueados
     *
     * @return
     */
    public void preguntarSiEstanBloqueados() {

        try {
            // envÃ­a el mensaje y espera respuestas
            String mensaje = Protocolo.PREGUNTO_SI_ESTAN_BLOQUEADOS.toString()+":"+padre.getNombreUsuario();
            DatagramPacket dp = new DatagramPacket(mensaje.getBytes(), mensaje.length());
           
                ms.send(dp);
                
                
            
            // acÃ¡ recoge las respuestas            
            byte[] buf = new byte[1000];
            DatagramPacket recv = new DatagramPacket(buf, buf.length);
            ms.receive(recv);
             // System.out.println("Paso 6");
            mensaje = new String(recv.getData(), 0);
            
            
        } catch (IOException ex) {
            Logger.getLogger(Buscador.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }

    void seguirBuscandoFotoMasParecida( int puntajeMinimo, String idFotoMasParecida )
    {
        try 
        {
            String mensaje = Protocolo.BLOQUEENSE.toString();
            DatagramPacket dp = new DatagramPacket(mensaje.getBytes(), mensaje.length());

            for (int i = 0; i < Compumundohipermegared.VECES_REPETIR_MENSAJES; i++) {
                ms.send(dp);
            }

            mensaje = Protocolo.CONTINUAR_MANDANDO_FOTO_MAS_PARECIDA.toString( );

            dp = new DatagramPacket( mensaje.getBytes( ), mensaje.length( ) );
            ms.send( dp );

            byte[] buf = new byte[1000];
            DatagramPacket recv = new DatagramPacket(buf, buf.length);
            ms.receive(recv);

            System.out.println("Esperando Respuestas");
            // ahora se queda esperando respuestas, 

            int numRespuestas = Compumundohipermegared.LIMITE_RESPUESTAS_FOTO_MAS_PARECIDA;
            int tiempoLimite = Compumundohipermegared.LIMITE_TIEMPO_RESPUESTA_FOTO_MAS_PARECIDA_SEGUNDOS;
            long tiempoInicial = System.currentTimeMillis();
            int numActualRespuestas = 0;

            boolean puedoSeguir = true;
            String idFotoMasParecida2 = idFotoMasParecida;

            while( puedoSeguir ) 
            {
                boolean numeroRespuestasAlcanzado = numRespuestas - numActualRespuestas <= 0;
                boolean tiempoLimiteAlcanzado = System.currentTimeMillis() - tiempoInicial > tiempoLimite * 1000;

                puedoSeguir = !(numeroRespuestasAlcanzado || tiempoLimiteAlcanzado);

                int puntajeMinimo2 = puntajeMinimo;

                if ( puedoSeguir )
                {
                    try 
                    {
                        ms.setSoTimeout(tiempoLimite * 1000);
                        ms.receive(recv);

                        mensaje = new String(recv.getData(), 0);

                        System.out.println("llego la respuesta: " + mensaje);

                        if (!mensaje.startsWith( Protocolo.CONTINUAR_MANDANDO_FOTO_MAS_PARECIDA.toString( ) ) )
                        {
                            numActualRespuestas++;

                            String[] partesResp = mensaje.split(":");
                            String idRecibido = partesResp[0];
                            String puntajeStr = partesResp[1];
                            int puntajeRecibido = Integer.parseInt(puntajeStr);
                            if (puntajeRecibido < puntajeMinimo2) {
                                puntajeMinimo2 = puntajeRecibido;
                                padre.actualizarPuntajeMinimo( puntajeMinimo2 );
                                idFotoMasParecida2 = idRecibido;
                                padre.actualizarIdFotoMasParecida( idFotoMasParecida2 );
                            }
                        }
                    } 
                    catch( Exception e )
                    {
                        
                    }
                }
            }

            System.out.println("Termine de obtener respuestas");

            mensaje = Protocolo.TERMINE_RECIBIR_FOTOS.toString();
            dp = new DatagramPacket(mensaje.getBytes(), mensaje.length());
            // envÃ­a el mensaje varias veces por si se pierfde
            for (int i = 0; i < Compumundohipermegared.VECES_REPETIR_MENSAJES; i++) {
                ms.send(dp);
            }

            padre.buscarPorID( idFotoMasParecida );
            
            padre.encontreLaFotoMasParecida( );
        } 
        catch( IOException e )
        {
            System.out.println("Se toteooooooooooooooooooooooooo");
            e.printStackTrace();
        }
    }
    
    public void enviarCertificado( )
    {     
        FileInputStream fis = null;
        try 
        {
            File myFile = new File("./data/"+padre.getNombreUsuario( ) +".cer" );
            byte[] mybytearray = new byte[(int) myFile.length()];
            out.println(Protocolo.AHI_VA_MI_CERTIFICADO.toString() + ":" + padre.getNombreUsuario( ) + ":" + mybytearray.length );
            System.out.println(Protocolo.AHI_VA_MI_CERTIFICADO.toString() + ":" + padre.getNombreUsuario( ) + ":" + mybytearray.length);
            fis = new FileInputStream(myFile);
            BufferedInputStream bis = new BufferedInputStream(fis);
            bis.read(mybytearray, 0, mybytearray.length);
            // envio el tamanio del archivo
           // out.println(Protocolo.AHI_VA_EL_ARCHIVO+":"+mybytearray.length);
            outputStream.write(mybytearray,0,mybytearray.length);
            outputStream.flush();
            System.out.println("ya lo envie");
        }
        catch(Exception ex) 
        {
            Logger.getLogger(Buscador.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }
    public void recibirCertificado(String mensaje) throws Exception
    {
        
                ////////////////////
                /// certificado!!!!!
                /////////////////////
                
                if(mensaje.startsWith(Protocolo.AHI_VA_MI_CERTIFICADO.toString()))
                {
                    System.out.println("Comienzo a recibir el certificado");
                    String rutaDestino = "./data/" + mensaje.split( ":" )[ 1 ]+".cer";
                    System.out.println(rutaDestino);
                    int tamanio = Integer.valueOf(mensaje.split(":")[2]);
                    int bytesRead;
                    int current = 0;
                    byte[] mybytearray = new byte[tamanio];
                    FileOutputStream fos = new FileOutputStream(rutaDestino);
                    
                    rutaCertDelQueEnvia=rutaDestino;

                    BufferedOutputStream bos = new BufferedOutputStream(fos);
                    do {
                        bytesRead = inputStream.read(mybytearray, current, (mybytearray.length - current));
                        System.out.println("He leido " + current + " bytes, y acabo de leer" + bytesRead);
                        if (bytesRead >= 0) {
                            current += bytesRead;
                        }
                    } while (bytesRead > 0);

                    System.out.println("Termine de recibir el certificado");
                    bos.write(mybytearray, 0, current);
                    bos.flush();


                    bos.close();
            
                }
               
    }
    public void recibirLlaveSimetrica( )
    {
         
            
          System.out.println("Recibiendo llave simetricas");
        try
        {
            String mensaje = in.readLine();
            System.out.println("llego el mensaje:" +mensaje);
            if( mensaje.startsWith( Protocolo.AHI_VA_MI_LLAVE_SIMETRICA.toString( ) ) )
            {
                mensaje = in.readLine( );
            
                ObjectInputStream ois = new ObjectInputStream( new FileInputStream( "./data/" + padre.getNombreUsuario( ) + ".llavePrivada" ) );
                PrivateKey privk = ( PrivateKey )ois.readObject( );

                System.out.println( "Ya cargue mi llave privada" );
                System.out.println( "Este es la contraseÃ±a que me llegÃ³" );
                System.out.println( mensaje );          
                System.out.println( mensaje.getBytes().length );
                
                
                mensaje= mensaje.substring(0, mensaje.length()-1);
                String[] strBytesRecibidos = mensaje.split(";");
                
                byte[] bytesRecibidos = new byte[strBytesRecibidos.length];
                
                for (int i = 0; i < strBytesRecibidos.length; i++) {
                    String string = strBytesRecibidos[i];
                    byte b = Byte.valueOf(string);
                    bytesRecibidos[i]=b;
                    
                    System.out.print(b+"\t");
                }
                
                System.out.println();
                

              
                 Rsa cifrador = new Rsa();
                cifrador.asignarLLavePrivada(privk);
                
      
                byte[] bytesDecifrados = ManejadorCertificados.bytesDescifrarTextoConLlavePublica(privk, bytesRecibidos);
    
                bytesLlaveDescifrada=bytesDecifrados;
               
                String llave = new String( bytesDecifrados );
                
                System.out.println( "Esta es la llave simetrica:" + llave );
                
                claveDescifrada= llave;
            }
        } 
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    private void recibirFirma() throws IOException {
        System.out.println("Comienzo a recibir la fr");
        String mensaje = in.readLine();
        String rutaDestino = "./data/firma";
        int tamanio = Integer.valueOf(mensaje.split(":")[1]);
        int bytesRead;
        int current = 0;
        byte[] mybytearray = new byte[tamanio];
        FileOutputStream fos = new FileOutputStream(rutaDestino);

        BufferedOutputStream bos = new BufferedOutputStream(fos);
        do {
            bytesRead = inputStream.read(mybytearray, current, (mybytearray.length - current));
            System.out.println("He leido " + current + " bytes, y acabo de leer" + bytesRead);
            if (bytesRead >= 0) {
                current += bytesRead;
            }
        } while (bytesRead > 0);

        System.out.println("Termine de recibir la firma");
        bos.write(mybytearray, 0, current);
        bos.flush();


        bos.close();
    }

    private boolean validarFirma() {
        return ManejadorCertificados.verificarFirmaObjeto("./data/firma", rutaFotoRecibida, null);
    }
}